package DEEPERsource.DEEPERsource.source.parser.stanfordparser;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.stanford.nlp.ling.Sentence;
import edu.stanford.nlp.ling.TaggedWord;
import edu.stanford.nlp.ling.Word;
import edu.stanford.nlp.parser.lexparser.LexicalizedParser;
import edu.stanford.nlp.process.DocumentPreprocessor;
import edu.stanford.nlp.trees.GrammaticalStructure;
import edu.stanford.nlp.trees.GrammaticalStructureFactory;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.TreePrint;
import edu.stanford.nlp.trees.TreebankLanguagePack;
import edu.stanford.nlp.trees.TypedDependency;
import edu.stanford.nlp.trees.WordStemmer;

/**
 * Wrapper for the Stanford parser, encapsulates the original library and provides the 
 * basic functionality that is necessary for DEEPER.
 * 
 *  However, I will use Sentence class through the application, since acts independently
 *  of other parser parts, and provides very nice functionality for word iteration and counting. 
 * @author tfayruzo
 *
 */
public class StanfordParserWrapper {
	
	private static Log _log = LogFactory.getLog(StanfordParserWrapper.class);

	private LexicalizedParser lp = new LexicalizedParser("resource/englishPCFG.ser.gz");
	//private TreebankLanguagePack tlp = new PennTreebankLanguagePack();
	
	//can take values:
	//dependencies
	//collapsedDependencies
	//typedDependenciesCCprocessed
	private String depType;

	private Tree parseTree;
	private GrammaticalStructure depTree;
	private Sentence<TaggedWord> sentence;
	
	public StanfordParserWrapper(int maxLength, String depType){
		this.depType = depType;
		lp.setOptionFlags(new String[] {"-maxLength", Integer.toString(maxLength)});
		//, "-outputFormat", "wordsAndTags"
	}
	
	public void restore(Tree parse, GrammaticalStructure dep){
		parseTree = parse;
		depTree = dep;
		sentence = parseTree.taggedYield();
	}
	
	public Collection<TypedDependency> getTypedDependencies(){
		if(parseTree != null){
			if(depTree == null){
				depTree = buildDependencyTree();				
			}
			if(depType.equals("typedDependencies")){
				Collection<TypedDependency> list = depTree.typedDependencies();
				if(!depTree.isConnected(list)){
					_log.error("Dependency tree has more than one root");
				}	
				return list;
			}else if(depType.equals("typedDependenciesCollapsed")){
				Collection<TypedDependency> list = depTree.typedDependenciesCollapsed();
				if(!depTree.isConnected(list)){
					_log.error("Dependency tree has more than one root");
				}	
				return list;
			}else if(depType.equals("typedDependenciesCCprocessed")){
				Collection<TypedDependency> list = depTree.typedDependenciesCCprocessed(false);
				if(!depTree.isConnected(list)){
					_log.error("Dependency tree has more than one root");
				}	
				return list;
			}
		}
		return null;
	}
	
	public GrammaticalStructure buildDependencyTree(){
		TreebankLanguagePack tlp = lp.getOp().tlpParams.treebankLanguagePack();
		GrammaticalStructureFactory factory = tlp.grammaticalStructureFactory();
		depTree = factory.newGrammaticalStructure(parseTree);
		return depTree;
	}
	
	public Tree getParse(String sentence) throws UnsupportedOperationException{
		//this.sentence = sentence;
		if(sentence == null){
			throw new UnsupportedOperationException("Sentence is undefined (probably dimeric interaction).");
		}
		DocumentPreprocessor dp = new DocumentPreprocessor(lp.getOp().tlpParams.treebankLanguagePack().getTokenizerFactory());
		List<Word> lst = dp.getWordsFromString((String) sentence);
		int i = 0;
		//redefining words with POS tags
		Pattern p = Pattern.compile("\\\\/[A-Z]+");
		for(Word word : lst){
			Matcher m = p.matcher(word.word());
			if(m.find()){
				String POStag = m.group();
				lst.set(i, new TaggedWord(word.word().replace(POStag, ""),POStag));
			}
			i++;
		}
		if (lp.parse(lst)) {
			parseTree = lp.getBestParse();
		}
		this.sentence = parseTree.taggedYield();
		depTree = buildDependencyTree();
		return parseTree;
	}
	
	//printer methods
	
	//prints parse tree to stdout
	public void printParseTree(){
		TreePrint treePrint = lp.getTreePrint();
		treePrint.printTree(parseTree);
	}
	
	//prints parse tree to string
	public String parseTree2str(){
		StringWriter strWriter = new StringWriter();
		PrintWriter writer = new PrintWriter(strWriter);
		TreePrint treePrint = lp.getTreePrint();
		treePrint.printTree(parseTree, writer);
		return strWriter.toString();
	}
	
	/**
	 * print to string
	 * @param formats oneline, penn, latexTree, words,
     *                wordsAndTags, rootSymbolOnly, dependencies,
	 *                typedDependencies, typedDependenciesCollapsed,
	 *                typedDependenciesCCprocessed, collocations
	 * @param options stem, lexicalize, markHeadNodes,
	 *                xml, removeTopBracket, transChinese,
	 *                includePunctuationDependencies, includeExtras
	 */
	public String print2str(String formats, String options){
		StringWriter strWriter = new StringWriter();
		PrintWriter writer = new PrintWriter(strWriter);
		TreebankLanguagePack tlp = lp.getOp().tlpParams.treebankLanguagePack();
		//ExtTreePrint treePrint = new ExtTreePrint(formats,options,tlp);
		TreePrint treePrint = new TreePrint(formats,options,tlp);
		treePrint.printTree(parseTree,writer);
		return strWriter.toString();
	}
	
	/**
	 * print to stdout
	 * @param formats oneline, penn, latexTree, words,
     *                wordsAndTags, rootSymbolOnly, dependencies,
	 *                typedDependencies, typedDependenciesCollapsed,
	 *                typedDependenciesCCprocessed, collocations
	 * @param options stem, lexicalize, markHeadNodes,
	 *                xml, removeTopBracket, transChinese,
	 *                includePunctuationDependencies, includeExtras
	 */
	public void print(String formats, String options){
		TreebankLanguagePack tlp = lp.getOp().tlpParams.treebankLanguagePack();
		//ExtTreePrint treePrint = new ExtTreePrint(formats,options,tlp);
		TreePrint treePrint = new TreePrint(formats,options,tlp);
		treePrint.printTree(parseTree);
	}
	
	public Tree getParseTree() {
		return parseTree;
	}
	
	public static void main(String[] args){
		//String sentence = "Bills on ports and immigration were submitted by Senator Brownback , Republican of Kansas";
		String sentence = "However, the mechanism by which activin/NNP exerts its inhibitory effects are not yet understood.";
		//String sentence = "In this clinical trial, children were randomly assigned to one of three groups and received either one single oral high dose of VA (200 000 IU) on the day of admission and subsequently a placebo daily until discharge or daily oral low doses of VA (5000 IU) from admission until discharge or a placebo daily from admission until discharge.";
		StanfordParserWrapper parser = new StanfordParserWrapper(70, "typedDependenciesCCprocessed");
		parser.getParse(sentence);
		parser.print("wordsAndTags, typedDependenciesCCprocessed","");
	}

	public Sentence<TaggedWord> getSentence() {
		return sentence;
	}
}